package com.shop.cloud.mall.admin.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import co.elastic.clients.elasticsearch.core.bulk.IndexOperation;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shop.cloud.common.core.constant.CacheConstants;
import com.shop.cloud.common.core.constant.CommonConstants;
import com.shop.cloud.common.core.util.R;
import com.shop.cloud.common.data.tenant.TenantContextHolder;
import com.shop.cloud.common.es.utils.ElasticSearchMapperBuilder;
import com.shop.cloud.common.es.utils.ElasticSearchUtil;
import com.shop.cloud.mall.admin.service.*;
import com.shop.cloud.mall.common.constant.MallConstants;
import com.shop.cloud.mall.common.entity.*;
import com.shop.cloud.mall.admin.mapper.GoodsSpuMapper;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * spu商品
 *
 * @author
 * @date 2019-08-12 16:25:10
 */
@Service
@AllArgsConstructor
public class GoodsSpuServiceImpl extends ServiceImpl<GoodsSpuMapper, GoodsSpu> implements GoodsSpuService {

	private final GoodsSkuService goodsSkuService;
	private final GoodsSpuSpecService goodsSpuSpecService;
	private final GoodsSkuSpecValueService goodsSkuSpecValueService;
	private final UserCollectService userCollectService;
	private final EnsureGoodsService ensureGoodsService;
	private final UserFootprintService userFootprintService;
	private final ElasticSearchUtil elasticSearchUtil;
	private final GoodsRecommendService goodsRecommendService;
	private final ShopInfoService shopInfoService;
	private final RedisTemplate redisTemplate;

	@SneakyThrows
	@Override
	@Transactional(rollbackFor = Exception.class)
	@Caching(evict = {
		@CacheEvict(value = CacheConstants.MALL_SHOPPINGCART_CACHE, allEntries = true),
		@CacheEvict(value = CacheConstants.MALL_GOODS_SPU, key = "#id")
	})
	public boolean removeById(Serializable id) {
		super.removeById(id);
		//删除SpuSpec、SkuSpecValue、Sku、用户收藏、用户足迹、商品推荐
		goodsSpuSpecService.remove(Wrappers.<GoodsSpuSpec>update().lambda()
				.eq(GoodsSpuSpec::getSpuId, id));
		goodsSkuSpecValueService.remove(Wrappers.<GoodsSkuSpecValue>update().lambda()
				.eq(GoodsSkuSpecValue::getSpuId, id));
		goodsSkuService.remove(Wrappers.<GoodsSku>update().lambda()
				.eq(GoodsSku::getSpuId, id));
		userCollectService.remove(Wrappers.<UserCollect>update().lambda()
				.eq(UserCollect::getType, MallConstants.COLLECT_TYPE_1)
				.eq(UserCollect::getRelationId, id));
		userFootprintService.remove(Wrappers.<UserFootprint>update().lambda()
				.eq(UserFootprint::getRelationId, id));
		goodsRecommendService.remove(Wrappers.<GoodsRecommend>update().lambda()
				.eq(GoodsRecommend::getSpuId, id));
		//ES删除
		elasticSearchUtil.deleteDocument(this.getEsSpuIndexName(), (String) id);
		return true;
	}

	@SneakyThrows
	@Override
	public IPage<GoodsSpu> page1(IPage<GoodsSpu> page, GoodsSpu goodsSpu) {
		return baseMapper.selectPage1(page, goodsSpu);
	}

	@SneakyThrows
	@Override
	@Transactional(rollbackFor = Exception.class)
	@CacheEvict(value = CacheConstants.MALL_SHOPPINGCART_CACHE, allEntries = true)
	public void shelf(String shelf, String ids) {
		GoodsSpu goodsSpu = new GoodsSpu();
		goodsSpu.setShelf(shelf);
		baseMapper.update(goodsSpu,Wrappers.<GoodsSpu>lambdaQuery()
				.in(GoodsSpu::getId, Convert.toList(ids)));
		//ES先删除
		List<FieldValue> listIds = new ArrayList<>();
		for (String s : ids.split(",")) {
			//清理该商品redis缓存
			redisTemplate.delete(StrUtil.format("{}:{}::{}", TenantContextHolder.getTenantId(), CacheConstants.MALL_GOODS_SPU, s));
			listIds.add(FieldValue.of(s));
		}
		TermsQueryField termsQueryField = new TermsQueryField.Builder()
				.value(listIds)
				.build();
		Query query =  new TermsQuery.Builder()
				.field("id")
				.terms(termsQueryField)
				.build()._toQuery();
		elasticSearchUtil.deleteDocumentByQuery(this.getEsSpuIndexName(), query);
		//ES再新增
		if(CommonConstants.YES.equals(shelf)){
			List<GoodsSpu> listSpu = baseMapper.selectList(Wrappers.<GoodsSpu>lambdaQuery()
					.eq(GoodsSpu::getShelf, CommonConstants.YES)
					.in(GoodsSpu::getId, Convert.toList(ids)));
			if(listSpu != null && listSpu.size() > 0){
				List<BulkOperation> operations = new ArrayList<>();
				listSpu.forEach(goodsSpu1 -> {
					IndexOperation<GoodsSpu> indexOperation = new IndexOperation.Builder<GoodsSpu>().id(goodsSpu1.getId()).document(goodsSpu1).build();
					operations.add(new BulkOperation.Builder().index(indexOperation).build());
				});
				elasticSearchUtil.bulkDocument(this.getEsSpuIndexName(), operations);
			}
		}
	}

	@SneakyThrows
	@Override
	@Transactional(rollbackFor = Exception.class)
	@CacheEvict(value = CacheConstants.MALL_SHOPPINGCART_CACHE, allEntries = true)
	public void verify(String verifyStatus, String verifyDetail, String ids) {
		GoodsSpu goodsSpu = new GoodsSpu();
		goodsSpu.setVerifyStatus(verifyStatus);
		goodsSpu.setVerifyDetail(verifyDetail);
		baseMapper.update(goodsSpu,Wrappers.<GoodsSpu>lambdaQuery()
				.in(GoodsSpu::getId, Convert.toList(ids)));
		//ES先删除
		List<FieldValue> listIds = new ArrayList<>();
		for (String s : ids.split(",")) {
			//清理该商品redis缓存
			redisTemplate.delete(StrUtil.format("{}:{}::{}", TenantContextHolder.getTenantId(), CacheConstants.MALL_GOODS_SPU, s));
			listIds.add(FieldValue.of(s));
		}
		TermsQueryField termsQueryField = new TermsQueryField.Builder()
				.value(listIds)
				.build();
		Query query =  new TermsQuery.Builder()
				.field("id")
				.terms(termsQueryField)
				.build()._toQuery();
		elasticSearchUtil.deleteDocumentByQuery(this.getEsSpuIndexName(), query);
		//ES再新增
		List<GoodsSpu> listSpu = baseMapper.selectList(Wrappers.<GoodsSpu>lambdaQuery()
				.eq(GoodsSpu::getShelf, CommonConstants.YES)
				.in(GoodsSpu::getId, Convert.toList(ids)));
		if(listSpu != null && listSpu.size() > 0){
			List<BulkOperation> operations = new ArrayList<>();
			listSpu.forEach(goodsSpu1 -> {
				IndexOperation<GoodsSpu> indexOperation = new IndexOperation.Builder<GoodsSpu>().id(goodsSpu1.getId()).document(goodsSpu1).build();
				operations.add(new BulkOperation.Builder().index(indexOperation).build());
			});
			elasticSearchUtil.bulkDocument(this.getEsSpuIndexName(), operations);
		}
	}

	@SneakyThrows
	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean save1(GoodsSpu goodsSpu) {
		ShopInfo shopInfo = shopInfoService.getById(goodsSpu.getShopId());
		goodsSpu.setSaleType(shopInfo.getSaleType());
		this.saveHanddler(goodsSpu);
		//ES新增
		if(CommonConstants.YES.equals(goodsSpu.getShelf())){
			elasticSearchUtil.index(this.getEsSpuIndexName(), goodsSpu.getId(), goodsSpu);
		}
		return true;
	}

	void saveHanddler(GoodsSpu goodsSpu){
		goodsSpu.setPriceDown(null);
		goodsSpu.setPriceUp(null);
		baseMapper.insert(goodsSpu);
		List<GoodsSku> listGoodsSku = goodsSpu.getSkus();
		if(listGoodsSku !=null && listGoodsSku.size()>0){
			//新增sku
			listGoodsSku.forEach(goodsSku -> {
				priceUpDown(goodsSpu,goodsSku);
				goodsSku.setShopId(goodsSpu.getShopId());
				goodsSku.setSpuId(goodsSpu.getId());
				goodsSkuService.save(goodsSku);
				goodsSku.setId(goodsSku.getId());
			});
			baseMapper.updateById(goodsSpu);
			if(MallConstants.SPU_SPEC_TYPE_1.equals(goodsSpu.getSpecType())){//多规格处理
				goodsSpu.setId(goodsSpu.getId());
				addSpec(goodsSpu);
			}
		}
		if(goodsSpu.getEnsureIds() != null && goodsSpu.getEnsureIds().size() > 0){//新增保障服务
			List<EnsureGoods> listEnsureGoods = goodsSpu.getEnsureIds()
					.stream().map(ensureId -> {
						EnsureGoods ensureGoods = new EnsureGoods();
						ensureGoods.setEnsureId(ensureId);
						ensureGoods.setSpuId(goodsSpu.getId());
						return ensureGoods;
					}).collect(Collectors.toList());
			ensureGoodsService.saveBatch(listEnsureGoods);
		}
		GoodsRecommend goodsRecommend = goodsSpu.getGoodsRecommend();
		if(goodsRecommend != null){//新增商品推荐
			goodsRecommend.setSpuId(goodsSpu.getId());
			goodsRecommend.setShopId(goodsSpu.getShopId());
			goodsRecommendService.save(goodsRecommend);
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public R saveBatch1(List<GoodsSpu> listGoodsSpu) {
		for (GoodsSpu item : listGoodsSpu) {
			item.setVerifyStatus(MallConstants.GOODS_STATUS_0);
//			if(MallConstants.FREIGHT_TYPE_2.equals(item.getFreightType())){
//				FreightTemplat freightTemplat = freightTemplatService.getById(item.getFreightTemplatId());
//				if(!item.getShopId().equals(freightTemplat.getShopId())){
//					return R.failed("商品【" + item.getName() +"】运费模板不正确");
//				}
//			}
			ShopInfo shopInfo = shopInfoService.getById(item.getShopId());
			item.setSaleType(shopInfo.getSaleType());
			this.saveHanddler(item);
		}
		return R.ok();
	}

	@SneakyThrows
	@Override
	@Transactional(rollbackFor = Exception.class)
	@Caching(evict = {
			@CacheEvict(value = CacheConstants.MALL_SHOPPINGCART_CACHE, allEntries = true),
			@CacheEvict(value = CacheConstants.MALL_GOODS_SPU, key = "#goodsSpu.id")
	})
	public boolean updateById1(GoodsSpu goodsSpu) {
		ShopInfo shopInfo = shopInfoService.getById(goodsSpu.getShopId());
		goodsSpu.setSaleType(shopInfo.getSaleType());
		goodsSpu.setPriceDown(null);
		goodsSpu.setPriceUp(null);
		List<GoodsSku> listGoodsSku = goodsSpu.getSkus();
		if(listGoodsSku !=null && listGoodsSku.size()>0){
			//先删除旧sku
			goodsSkuService.remove(Wrappers.<GoodsSku>update().lambda()
					.eq(GoodsSku::getSpuId, goodsSpu.getId()));
			//新增sku
			listGoodsSku.forEach(goodsSku -> {
				priceUpDown(goodsSpu,goodsSku);
				goodsSku.setShopId(goodsSpu.getShopId());
				goodsSku.setSpuId(goodsSpu.getId());
				goodsSku.setTenantId(null);
				if(!goodsSkuService.saveOrUpdate(goodsSku)){//更新库存
					throw new RuntimeException("请重新提交");
				}
				goodsSku.setId(goodsSku.getId());
			});
			baseMapper.updateById(goodsSpu);
			//统一删除SpuSpec、SkuSpecValue
			goodsSpuSpecService.remove(Wrappers.<GoodsSpuSpec>update().lambda()
					.eq(GoodsSpuSpec::getSpuId, goodsSpu.getId()));
			goodsSkuSpecValueService.remove(Wrappers.<GoodsSkuSpecValue>update().lambda()
					.eq(GoodsSkuSpecValue::getSpuId, goodsSpu.getId()));
			if(MallConstants.SPU_SPEC_TYPE_1.equals(goodsSpu.getSpecType())) {//多规格处理
				addSpec(goodsSpu);
			}
			//修改保障服务
			ensureGoodsService.remove(Wrappers.<EnsureGoods>update().lambda()
					.eq(EnsureGoods::getSpuId, goodsSpu.getId()));
			if(goodsSpu.getEnsureIds() != null && goodsSpu.getEnsureIds().size() > 0){
				List<EnsureGoods> listEnsureGoods = new ArrayList<>();
				goodsSpu.getEnsureIds().forEach(ensureId -> {
					EnsureGoods ensureGoods = new EnsureGoods();
					ensureGoods.setEnsureId(ensureId);
					ensureGoods.setSpuId(goodsSpu.getId());
					listEnsureGoods.add(ensureGoods);
				});
				ensureGoodsService.saveBatch(listEnsureGoods);
			}
			GoodsRecommend goodsRecommend = goodsSpu.getGoodsRecommend();
			if(goodsRecommend != null){//修改商品推荐
				goodsRecommend.setSpuId(goodsSpu.getId());
				goodsRecommend.setShopId(goodsSpu.getShopId());
				goodsRecommendService.saveOrUpdate(goodsRecommend);
			}
		}
		//ES修改：先删后增
		elasticSearchUtil.deleteDocument(this.getEsSpuIndexName(), goodsSpu.getId());
		if(CommonConstants.YES.equals(goodsSpu.getShelf())){
			GoodsSpu goodsSpu1 = baseMapper.selectById(goodsSpu.getId());
			elasticSearchUtil.index(this.getEsSpuIndexName(), goodsSpu.getId(), goodsSpu1);
		}
		return true;
	}

	@SneakyThrows
	@Override
	@Caching(evict = {
			@CacheEvict(value = CacheConstants.MALL_SHOPPINGCART_CACHE, allEntries = true),
			@CacheEvict(value = CacheConstants.MALL_GOODS_SPU, key = "#goodsSpu.id")
	})
	public boolean updateById2(GoodsSpu goodsSpu) {
		baseMapper.updateById(goodsSpu);
		//ES修改：先删后增
		elasticSearchUtil.deleteDocument(this.getEsSpuIndexName(), goodsSpu.getId());
		GoodsSpu goodsSpu1 = baseMapper.selectById(goodsSpu.getId());
		if(CommonConstants.YES.equals(goodsSpu1.getShelf())){
			elasticSearchUtil.index(this.getEsSpuIndexName(), goodsSpu.getId(), goodsSpu1);
		}
		return true;
	}

	@Override
	public GoodsSpu getById1(String id) {
		GoodsSpu goodsSpu = baseMapper.selectById1(id);
		//获取商品推荐·
		goodsSpu.setGoodsRecommend(
				goodsRecommendService.getOne(Wrappers.<GoodsRecommend>query().lambda()
						.eq(GoodsRecommend::getSpuId, id))
		);
		return goodsSpu;
	}

	/**
	 * 多规格处理
	 * @param goodsSpu
	 */
	void addSpec(GoodsSpu goodsSpu){
		//新增SpuSpec
		List<GoodsSpuSpec> listGoodsSpuSpec = goodsSpu.getSpuSpec().stream().map(spuSpec->{
			GoodsSpuSpec goodsSpuSpec = new GoodsSpuSpec();
			goodsSpuSpec.setSpuId(goodsSpu.getId());
			goodsSpuSpec.setSpecId(spuSpec.getId());
			return goodsSpuSpec;
		}).collect(Collectors.toList());
		for(int i = 0; i<listGoodsSpuSpec.size(); i++) {
			listGoodsSpuSpec.get(i).setSort(i);//设置排序字段
		}
		goodsSpuSpecService.saveBatch(listGoodsSpuSpec);
		//新增SkuSpecValue
		List<GoodsSkuSpecValue> listGoodsSkuSpecValue = new ArrayList<>();
		goodsSpu.getSkus().forEach(goodsSku -> {
			goodsSku.getSpecs().forEach(goodsSkuSpecValue -> {
				goodsSkuSpecValue.setSpuId(goodsSpu.getId());
				goodsSkuSpecValue.setSkuId(goodsSku.getId());
				listGoodsSkuSpecValue.add(goodsSkuSpecValue);
			});
		});
		for(int i = 0; i<listGoodsSkuSpecValue.size(); i++) {
			listGoodsSkuSpecValue.get(i).setSort(i);//设置排序字段
		}
		goodsSkuSpecValueService.saveBatch(listGoodsSkuSpecValue);
	}

	/**
	 * 获取商品最高最低价
	 * @param goodsSpu
	 * @param goodsSku
	 */
	void priceUpDown(GoodsSpu goodsSpu,GoodsSku goodsSku){
		if(CommonConstants.YES.equals(goodsSku.getEnable())){
			BigDecimal priceDown = goodsSpu.getPriceDown();
			if(priceDown == null || priceDown.compareTo(goodsSku.getSalesPrice()) == 1){
				goodsSpu.setPriceDown(goodsSku.getSalesPrice());
			}
			BigDecimal priceUp = goodsSpu.getPriceUp();
			if(priceUp == null || priceUp.compareTo(goodsSku.getSalesPrice()) == -1){
				goodsSpu.setPriceUp(goodsSku.getSalesPrice());
			}
		}
	}

	@Override
	public void esInit(String esSpuIndexName) {
		try {
			BooleanResponse b = elasticSearchUtil.existsIndex(esSpuIndexName);
			//先判断是否存在
			if(b.value()){
				//删除索引库
				elasticSearchUtil.deleteIndex(esSpuIndexName);
			}
			//新增索引库
			Map<String, Property> propertyMap = ElasticSearchMapperBuilder.getGoodsSpuMapper();
			elasticSearchUtil.createIndex(esSpuIndexName, 1, propertyMap);
			//初始化文档数据
			List<GoodsSpu> listSpu = baseMapper.selectList(Wrappers.<GoodsSpu>query().lambda().eq(GoodsSpu::getShelf, CommonConstants.YES));
			if(listSpu != null && listSpu.size() > 0){
				List<BulkOperation> operations = new ArrayList<>();
				listSpu.forEach(goodsSpu1 -> {
					IndexOperation<GoodsSpu> indexOperation = new IndexOperation.Builder<GoodsSpu>().id(goodsSpu1.getId()).document(goodsSpu1).build();
					operations.add(new BulkOperation.Builder().index(indexOperation).build());
				});
				elasticSearchUtil.bulkDocument(esSpuIndexName, operations);
			}
		}catch (Exception e){
			e.printStackTrace();
		}

	}

	@Override
	public String getEsSpuIndexName(){
		return StrUtil.format("{}_{}", "goods_spu", TenantContextHolder.getTenantId());
	}
}
